package goes

import (
	"encoding/json"
	"fmt"
	"io"
	"reflect"
	"slices"
	"strings"
)

func AutoGenMappingAndSetting(model interface{}) io.Reader {

	properties := autoGenProperties(model)

	mapping := _IndexCreator(
		_SettingIK(),
		_MappingProperties(properties...))

	j, _ := json.MarshalIndent(mapping, "", "  ")
	fmt.Println(string(j))

	js, _ := json.Marshal(mapping)
	return strings.NewReader(string(js))

}

func AutoGenMapping(model interface{}) io.Reader {

	properties := autoGenProperties(model)

	mapping := _MappingProperties(properties...)

	j, _ := json.MarshalIndent(mapping, "", "  ")
	fmt.Println(string(j))

	js, _ := json.Marshal(mapping)
	return strings.NewReader(string(js))
}

// 解析标签函数
func parseTag(tag string) map[string]string {
	result := make(map[string]string)
	parts := strings.Split(tag, ";")
	for _, part := range parts {
		keyValue := strings.SplitN(part, ":", 2) // 使用 SplitN 按":"分割，最多分成2部分
		if len(keyValue) == 2 {
			result[strings.TrimSpace(keyValue[0])] = strings.TrimSpace(keyValue[1])
		}
	}
	return result
}

func autoGenProperties(model interface{}) []map[string]interface{} {

	typ := reflect.TypeOf(model)
	properties := make([]map[string]interface{}, 0)
	for i := 0; i < typ.NumField(); i++ {
		field := typ.Field(i)
		key := field.Name

		tagMap := parseTag(field.Tag.Get("es"))
		if tagMap["type"] != "" {
			if tagMap["type"] == "text" && tagMap["analyzer"] == "" {
				tagMap["analyzer"] = "ik_max_word"
			}
			properties = append(properties, _Property(key, tagMap["type"], tagMap["analyzer"]))
			continue
		}

		fieldType := field.Type.Name()
		if fieldType == "bool" {
			properties = append(properties, _Property(key, "keyword", ""))
		} else if fieldType == "int" || fieldType == "int64" || fieldType == "int32" || fieldType == "float32" || fieldType == "float64" ||
			fieldType == "int16" || fieldType == "int8" {
			properties = append(properties, _Property(key, "long", ""))
		} else if fieldType == "GeoString" {
			properties = append(properties, _Property(key, "geo_point", ""))
		} else if fieldType == "Time" || fieldType == "DeletedAt" {
			properties = append(properties, _Property(key, "date", ""))
		} else {
			properties = append(properties, _PropertyIK(key, "text"))
		}
	}
	return properties
}

func _IsAnyEmpty(value any) (reflect.Type, bool) {
	if value == nil {
		return nil, true
	}
	reflectType := reflect.TypeOf(value)

	if reflectType == nil {
		return nil, true
	}
	if reflectType.Kind() == reflect.String {
		return reflectType, value.(string) == ""
	}

	return reflectType, reflect.ValueOf(value).IsNil()
}

func _Match[T any](key string, value T) map[string]interface{} {
	if _, ie := _IsAnyEmpty(value); ie {
		return nil
	}

	matchQuery := map[string]interface{}{
		"match": map[string]interface{}{
			key: value,
		},
	}
	return matchQuery
}

func _MatchAll() map[string]interface{} {
	return map[string]interface{}{
		"match_all": map[string]interface{}{},
	}
}

func _MultiMatch[T any](keys []string, value T, fuzz, minRate, operator string) map[string]interface{} {
	if _, ie := _IsAnyEmpty(value); ie {
		return nil
	}
	multiMatchQuery := map[string]interface{}{
		"multi_match": map[string]interface{}{
			"query":                value,
			"fields":               keys,
			"fuzziness":            fuzz,
			"minimum_should_match": minRate,
			"operator":             operator,
		},
	}
	return multiMatchQuery
}

func _MatchPhrase[T any](key string, value T) map[string]interface{} {
	if _, ie := _IsAnyEmpty(value); ie {
		return nil
	}
	matchPhraseQuery := map[string]interface{}{
		"match_phrase": map[string]interface{}{
			key: value,
		},
	}
	return matchPhraseQuery
}

func _Term[T any](key string, value T) map[string]interface{} {
	_, ie := _IsAnyEmpty(value)
	if ie {
		return nil
	}

	// if rt.Name() == "string" {
	// 	key = key + ".keyword"
	// }

	termQuery := map[string]interface{}{
		"term": map[string]interface{}{
			key: map[string]interface{}{
				"value": value,
			},
		},
	}
	return termQuery
}

func _Should(queries ...map[string]interface{}) map[string]interface{} {
	if len(queries) == 0 {
		return nil
	}
	shouldList := make([]map[string]interface{}, 0, len(queries))
	for i := range queries {
		if queries[i] == nil {
			continue
		}
		shouldList = append(shouldList, queries[i])
	}

	if len(shouldList) == 0 {
		return nil
	}
	sholdQuery := map[string]interface{}{
		"bool": map[string]interface{}{
			"should": shouldList,
		},
	}
	return sholdQuery
}

func _ShouldTerm[T any](key string, values ...T) map[string]interface{} {
	if len(values) == 0 {
		return nil
	}
	shouldList := make([]map[string]interface{}, 0, len(values))
	for _, value := range values {
		shouldList = append(shouldList, _Term(key, value))
	}
	return _Should(shouldList...)
}

func _ShouldMatch[T any](key string, values ...T) map[string]interface{} {
	if len(values) == 0 {
		return nil
	}
	shouldList := make([]map[string]interface{}, 0, len(values))
	for _, value := range values {
		shouldList = append(shouldList, _Match(key, value))
	}
	return _Should(shouldList...)
}

func _ShouldMatchPhrase[T any](key string, values ...T) map[string]interface{} {
	if len(values) == 0 {
		return nil
	}
	shouldList := make([]map[string]interface{}, 0, len(values))
	for _, value := range values {
		shouldList = append(shouldList, _MatchPhrase(key, value))
	}
	return _Should(shouldList...)
}

func _Must(queries ...map[string]interface{}) map[string]interface{} {
	if len(queries) == 0 {
		return nil
	}
	mustList := make([]map[string]interface{}, 0, len(queries))
	for i := range queries {
		if queries[i] == nil {
			continue
		}
		mustList = append(mustList, queries[i])
	}
	mustQuery := map[string]interface{}{
		"bool": map[string]interface{}{
			"must": mustList,
		},
	}
	return mustQuery
}

func _Bool(musts, shoulds, mustNots []map[string]interface{}) map[string]interface{} {
	musts = slices.DeleteFunc(musts, func(v map[string]interface{}) bool { return v == nil })
	shoulds = slices.DeleteFunc(shoulds, func(v map[string]interface{}) bool { return v == nil })
	mustNots = slices.DeleteFunc(mustNots, func(v map[string]interface{}) bool { return v == nil })

	boolMap := make(map[string]interface{})
	if len(musts) > 0 {
		boolMap["must"] = musts
	}
	if len(shoulds) > 0 {
		boolMap["should"] = shoulds
	}
	if len(mustNots) > 0 {
		boolMap["must_not"] = mustNots
	}

	boolQuery := map[string]interface{}{
		"bool": boolMap,
	}

	return boolQuery
}

func _MustTerm[T any](key string, values ...T) map[string]interface{} {
	if len(values) == 0 {
		return nil
	}
	mustList := make([]map[string]interface{}, 0, len(values))
	for _, value := range values {
		mustList = append(mustList, _Term(key, value))
	}
	return _Must(mustList...)
}

func _MustMatch[T any](key string, values ...T) map[string]interface{} {
	if len(values) == 0 {
		return nil
	}
	mustList := make([]map[string]interface{}, 0, len(values))
	for _, value := range values {
		mustList = append(mustList, _Match(key, value))
	}
	return _Must(mustList...)
}

func _Query(from, size int, query map[string]interface{}) map[string]interface{} {
	return map[string]interface{}{
		"query": query,
		"sort": map[string]interface{}{
			"_score": map[string]interface{}{
				"order": "desc",
			},
		},
		"from":             from,
		"size":             size,
		"track_total_hits": true, // 添加这一行以获取总记录数
	}

}

func _IndexCreator(settings map[string]interface{}, mappings map[string]interface{}) map[string]interface{} {
	return map[string]interface{}{
		"settings": settings,
		"mappings": mappings,
	}
}

func _SettingIK() map[string]interface{} {
	return map[string]interface{}{
		"analysis": map[string]interface{}{
			"analyzer": map[string]interface{}{
				"ik_max_word": map[string]interface{}{
					"type":      "custom",
					"tokenizer": "ik_max_word",
				},
			},
		},
	}
}

func _MappingIKTemplate() map[string]interface{} {
	return map[string]interface{}{
		"dynamic_templates": []map[string]interface{}{
			{
				"text_fields": map[string]interface{}{
					"match":              "*",    // 匹配所有字段
					"match_mapping_type": "text", // 匹配类型为 text
					"mapping": map[string]interface{}{
						"type":     "text",
						"analyzer": "ik_max_word", // 设置使用 IK 分词器
					},
				},
			},
		},
	}
}

func _MappingProperties(properties ...map[string]interface{}) map[string]interface{} {
	if len(properties) == 0 {
		return nil
	}

	proMap := make(map[string]interface{})
	for i := range properties {
		for k, v := range properties[i] {
			proMap[k] = v
		}
	}

	return map[string]interface{}{
		"properties": proMap,
	}
}

func _Property(_key, _type, _analyzer string) map[string]interface{} {
	if _analyzer == "" {
		return map[string]interface{}{
			_key: map[string]interface{}{
				"type": _type,
			},
		}
	}
	return map[string]interface{}{
		_key: map[string]interface{}{
			"type":     _type,
			"analyzer": _analyzer,
		},
	}
}

func _PropertyIK(_key, _type string) map[string]interface{} {
	return _Property(_key, _type, "ik_max_word")
}

func _MergeMapping(mappings ...map[string]interface{}) map[string]interface{} {
	if len(mappings) == 0 {
		return nil
	}

	mergedMap := make(map[string]interface{})
	for i := range mappings {
		for k, v := range mappings[i] {
			mergedMap[k] = v
		}
	}

	return mergedMap
}

type GeoPoint struct {
	Lat float64 `json:"lat"`
	Lon float64 `json:"lon"`
}

type GeoDistance struct {
	GeoPoint
	Distance string `json:"distance"` // 距离格式 10km 100m
}

func _GeoDistance(field string, geo GeoDistance) map[string]interface{} {
	if geo.Distance == "" || geo.Lat == 0 || geo.Lon == 0 {
		return nil
	}
	return map[string]interface{}{
		"geo_distance": map[string]interface{}{
			"distance": geo.Distance,
			field: map[string]interface{}{
				"lat": geo.Lat,
				"lon": geo.Lon,
			},
		},
	}
}

func _MustGeoDistance(field string, geos ...GeoDistance) map[string]interface{} {
	if len(geos) == 0 {
		return nil
	}

	mustList := make([]map[string]interface{}, 0, len(geos))
	for _, geo := range geos {
		mustList = append(mustList, _GeoDistance(field, geo))
	}
	return _Must(mustList...)
}

func _ShouldGeoDistance(field string, geos ...GeoDistance) map[string]interface{} {
	if len(geos) == 0 {
		return nil
	}
	shouldList := make([]map[string]interface{}, 0, len(geos))
	for _, geo := range geos {
		shouldList = append(shouldList, _GeoDistance(field, geo))
	}
	return _Should(shouldList...)
}

func _GeoPolygon(field string, points ...GeoPoint) map[string]interface{} {
	if len(points) == 0 {
		return nil
	}

	// 遍历输入的点
	geoPoints := make([]map[string]float64, 0, len(points))
	for _, point := range points {
		if point.Lat == 0 || point.Lon == 0 {
			continue
		}
		geoPoint := map[string]float64{
			"lat": point.Lat,
			"lon": point.Lon,
		}
		geoPoints = append(geoPoints, geoPoint)
	}

	return map[string]interface{}{
		"geo_polygon": map[string]interface{}{
			field: map[string]interface{}{
				"points": geoPoints,
			},
		},
	}
}

/*
"geo_polygon": {
	"Location": {
	  "points": [
		{ "lat": 40.7128, "lon": -74.0060 }, // Point 1
		{ "lat": 40.7328, "lon": -74.0060 }, // Point 2
		{ "lat": 40.7328, "lon": -73.9860 }, // Point 3
		{ "lat": 40.7128, "lon": -73.9860 }, // Point 4
		{ "lat": 40.7128, "lon": -74.0060 }  // Closing point to form a polygon
	  ]
	}
  }
}
*/
